#pragma once
// clang-format off
#include <cstdint>
#include "cib/cib.hpp"
#include <stdx/ct_string.hpp>
#include <stdx/ct_conversions.hpp>
#include "etl/singleton.h"
#include "cib/cib_config.hpp"
// clang-format on
namespace cib {
namespace internal {

template <typename TService, typename TFunc, TFunc func> struct TComponent {
  constexpr static auto config = cib::extend<TService>(func);
};

template <typename TLamda, TLamda lamda> struct Callback {
  static constexpr TLamda callback = lamda;
};
template <typename TService, typename... TCallback> struct Feature {
  constexpr static auto config = cib::config(
      cib::exports<TService>, cib::extend<TService>(TCallback::callback...));
};

template <typename TService> struct Export {
  static constexpr auto config = cib::exports<TService>;
};

template <typename TService, typename... TComponents> struct Config {
  static constexpr auto config =
      cib::components<Export<TService>, TComponents...>;
};

template <typename... TFeature> struct Project {
  static constexpr auto config = cib::components<TFeature...>;
};
template <typename TService> struct Nexus {
  template <typename... TCallbacks> Nexus(TCallbacks... callbacks) {
    static cib::nexus<
        Config<TService, TComponent<TService, TCallbacks, callbacks>...>>
        n;
    n.init();
  }
};
template <size_t id, typename... TArgs>
struct Connected : public cib::callback_meta<TArgs...> {
  inline static bool is_valid{false};
};
} // namespace internal

/// Connect callback to the service. The callback must be passed as a lamda
template <typename TService, typename... TCallbacks>
[[maybe_unused]] auto Connect(TCallbacks... callbacks) {
  TService::Connect(callbacks...);
}

template <typename TService, typename... TLamda>
static constexpr auto Extend(TLamda... lamda) {
  return internal::Feature<TService, internal::Callback<TLamda, lamda>...>{};
}

template <typename... TExtend> static auto Connect(TExtend...) {
  static cib::nexus<internal::Project<TExtend...>> n{};
  n.init();
};
template <typename TService, typename... Args> auto CallService(Args... args) {
  TService::Call(args...);
}

template <stdx::ct_string name, typename... TArg>
struct ServiceDef : public cib::callback_meta<TArg...> {
public:
  using Self = ServiceDef;
  template <size_t id> using Connected = internal::Connected<id, TArg...>;
#if CONFIG_CIB_ENABLE_MULTI_CALLS
  template <size_t... Idx>
  static auto DispatchCall(std::index_sequence<Idx...>,
                           auto &&...args) -> void {
    const size_t id = m_valid_count - 1;
    [[maybe_unused]] int dummy[] = {
        (Idx < id ? (cib::service<Connected<Idx>>(args...), 0) : 0)...};
  }
  template <size_t... Idx>
  static auto DispatchConnect(std::index_sequence<Idx...>,
                              auto &&...lambdas) -> void {
    ((m_valid_count == Idx &&
      (etl::singleton<internal::Nexus<Connected<Idx - 1>>>::create(lambdas...),
       ++m_valid_count, true)) ||
     ...);
  }
#endif
  template <typename... Args> static constexpr auto Call(auto... args) {
    if (IsValid()) {
      cib::service<Self>(args...);
#if CONFIG_ENABLE_CIB_MULTI_CALLS
      DispatchCall(std::make_index_sequence<CONFIG_CIB_MAX_CALLS>{}, args...);
#endif
    }
  }
  static auto Connect(auto... lambdas) {
    if (m_valid_count == 0) {
      ++m_valid_count;
      [[maybe_unused]] static internal::Nexus<Self> n{lambdas...};
    } else {
#if CONFIG_ENABLE_CIB_MULTI_CALLS
      DispatchConnect(std::make_index_sequence<CONFIG_CIB_MAX_CALLS>{},
                      lambdas...);
#endif
    }
  }
  static auto IsValid() -> bool { return m_valid_count != 0; }
  static auto Init() {
    if (m_valid_count == 0) {
      m_valid_count = 1;
    }
  }

private:
  inline static uint8_t m_valid_count{0};
};

template <typename TDerived, typename... TArgs>
struct ServiceTypeDef : public cib::callback_meta<TArgs...> {
  using Self = TDerived;
  static constexpr auto Call(auto... args) {
    if (IsValid()) {
      cib::service<Self>(args...);
    }
  }
  static auto Connect(auto... lambdas) {
    if (m_valid_count == 0) {
      ++m_valid_count;
      [[maybe_unused]] static internal::Nexus<Self> n{lambdas...};
    }
  }
  static auto IsValid() -> bool { return m_valid_count != 0; }
  static auto Init() {
    if (m_valid_count == 0) {
      m_valid_count = 1;
    }
  }

private:
  inline static uint8_t m_valid_count{0};
};
template <stdx::ct_string name, typename... TAgrs> constexpr auto Service() {
  return ServiceDef<name, TAgrs...>{};
}

#define CIB_SERVICE_DEFINE(__NAME__, ...)                                      \
  struct __NAME__                                                              \
      : public cib::ServiceTypeDef<__NAME__ __VA_OPT__(, ) __VA_ARGS__> {};

#define CIB_SERVICE_TYPE_DEFINE(...) CIB_SERVICE_DEFINE(__VA_ARGS__)
#define CIB_SERVICE(name, ...)                                                 \
  static constexpr auto name = cib::Service<#name __VA_OPT__(, ) __VA_ARGS__>();
#define CIB_SVC(...) CIB_SERVICE(__VA_ARGS__)
} // namespace cib
